package com.coder.util

import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.Application
import android.content.Context
import android.os.Build
import android.os.Process
import java.io.FileInputStream
import java.nio.charset.StandardCharsets

/**
 * @description 进程工具类
 * @author yuhus
 * @date 2021/12/10 11:07
 */
object ProcessUtil {
    private lateinit var mCurrentProcessName: String

    fun getProcessName(context: Context): String {
        if (this::mCurrentProcessName.isInitialized && mCurrentProcessName.isNotEmpty()) {
            return mCurrentProcessName
        }

        getProcessNameByApplication()?.let {
            mCurrentProcessName = it
            return it
        }

        getProcessNameByActivityThread()?.let {
            mCurrentProcessName = it
            return it
        }

        getProcessNameByActivityManager(context)?.let {
            mCurrentProcessName = it
            return it
        }

        getProcessNameByFile()?.let {
            mCurrentProcessName = it
            return it
        }

        return ""
    }

    /**
     * 通过Application新的API获取进程名，效率最高
     */
    fun getProcessNameByApplication(): String? {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            Application.getProcessName()
        } else {
            null
        }
    }

    /**
     * 通过反射ActivityThread获取当前进程名，避免IPC
     */
    @SuppressLint("PrivateApi", "DiscouragedPrivateApi")
    fun getProcessNameByActivityThread(): String? {
        kotlin.runCatching {
            val declareMethod = Class.forName(
                "android.app.ActivityThread",
                false,
                Application::class.java.classLoader
            ).getDeclaredMethod("currentProcessName")
            declareMethod.isAccessible = true
            return declareMethod.invoke(null) as? String
        }
        return null
    }

    /**
     * 通过ActivityManager获取当前进程名，IPC
     */
    fun getProcessNameByActivityManager(context: Context): String? {
        val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        // 这里可能会崩溃 系统的服务可能已经挂了 所以获取不到的时候返回空
        val runningApps = kotlin.runCatching { am.runningAppProcesses }.getOrNull() ?: return null
        for (proInfo in runningApps) {
            if (proInfo.pid == Process.myPid() && proInfo.processName != null) {
                return proInfo.processName
            }
        }
        return null
    }

    /**
     * 通过文件查看，类似 adb shell cat proc/{pid}/cmdline
     */
    fun getProcessNameByFile(): String? {
        var input: FileInputStream? = null
        try {
            val fn = "/proc/self/cmdline"
            input = FileInputStream(fn)
            val buffer = ByteArray(256)
            var len = 0
            var b: Int
            while (input.read().also { b = it } > 0 && len < buffer.size) {
                buffer[len++] = b.toByte()
            }
            if (len > 0) {
                return String(buffer, 0, len, StandardCharsets.UTF_8)
            }
        } catch (e: Throwable) {
            e.printStackTrace()
        } finally {
            input?.runCatching { close() }
        }
        return null
    }
}